package com.tencreat.socket;

import cn.hutool.core.util.IdUtil;
import com.tencreat.common.core.domain.entity.SysUser;
import com.tencreat.common.core.domain.model.AccountLogin;
import com.tencreat.common.core.domain.model.LoginUser;
import com.tencreat.common.core.service.TokenService;
import com.tencreat.common.utils.ip.IpUtils;
import com.tencreat.service.mall.IMallMessageService;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * web-socket
 * @author zixin
 */
@ServerEndpoint(path = "/web-socket/{userId}", host = "${ws.host}", port = "${ws.port}", childOptionSoKeepalive = "true")
public class NettyWebSocket {

    private Logger logger = LoggerFactory.getLogger(NettyWebSocket.class);

    @Autowired
    private IMallMessageService messageService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public static Map<String, Session> clients = new HashMap<>();

    @BeforeHandshake
    public void handshake(Session session, HttpHeaders headers, @PathVariable String userId, @RequestParam String token){
        String ip = IpUtils.getWebSocketIpAddress(session, headers);
        logger.info("用户ID：" + userId + "   token：" + token + "ip：" + ip);
        // 鉴权
        LoginUser loginUser = tokenService.getLoginUser(token);
        logger.info("loginUser---{}",loginUser);
        if (loginUser != null) {
            SysUser user = loginUser.getUser();
            AccountLogin appUser = loginUser.getAccountLogin();
            if (user != null) {
                if (userId.equals("" + user.getUserId())) {
                    tokenService.verifyToken(loginUser);
                    return;
                }
            }
            if (appUser != null) {
                if (userId.equals("" + appUser.getUuid())) {
                    tokenService.verifyToken(loginUser);
                    return;
                }
            }
        }
        logger.error("权限校验失败：" + userId);
        session.close();
    }

    @OnOpen
    public void onOpen(Session session, @PathVariable String userId){
        // 关闭旧连接
        Session sessionOld = clients.get(userId);
//        if (sessionOld != null) {
//            sessionOld.close();
//        }
        String key = userId + ":" + IdUtil.objectId();
        session.setAttribute("userId", userId);
        session.setAttribute("nowTime", LocalDateTime.now());
        clients.put(key, session);

        // 清除无用连接（每隔2分钟）
        threadPoolTaskExecutor.execute(() -> {
            messageService.clearUnSocket(clients);
        });
    }

    public static boolean isExist(String joinUserId) {
        for (String key : clients.keySet()) {
            Session session = clients.get(key);
            String userId = session.getAttribute("userId");
            if (userId.equals(joinUserId)) {
                return true;
            }
        }

        return false;
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        // 接收到消息
        messageService.sendMessage(session,session.getAttribute("userId"), message);
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        clients.remove(session.getAttribute("userId"), session);
        System.out.println("one connection closed");
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        clients.remove(session.getAttribute("userId"), session);
        throwable.printStackTrace();
    }

    @OnBinary
    public void onBinary(Session session, byte[] bytes) {
        for (byte b : bytes) {
            System.out.println(b);
        }
        session.sendBinary(bytes);
    }

    @OnEvent
    public void onEvent(Session session, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            switch (idleStateEvent.state()) {
                case READER_IDLE:
                    System.out.println("read idle");
                    break;
                case WRITER_IDLE:
                    System.out.println("write idle");
                    break;
                case ALL_IDLE:
                    System.out.println("all idle");
                    break;
                default:
                    break;
            }
        }
    }

}
